package org.rollinitiative.d20tools.arena;

import java.util.ArrayList;
import java.util.HashMap;

import javax.xml.namespace.QName;

import org.apache.commons.configuration.ConfigurationException;
import org.apache.commons.configuration.XMLConfiguration;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.log4j.xml.DOMConfigurator;
import org.rollinitiative.d20.dice.Dice;
import org.rollinitiative.d20.encounter.Action;
import org.rollinitiative.d20.encounter.ActionSet;
import org.rollinitiative.d20.encounter.Combatant;
import org.rollinitiative.d20.encounter.Encounter;
import org.rollinitiative.d20.encounter.EncounterAgent;
import org.rollinitiative.d20.encounter.EncounterListener;
import org.rollinitiative.d20.encounter.Faction;
import org.rollinitiative.d20.encounter.combat.actions.Attack;
import org.rollinitiative.d20.encounter.combat.actions.MeleeAttack;
import org.rollinitiative.d20.entity.Ability;
import org.rollinitiative.d20.entity.Actor;
import org.rollinitiative.d20.entity.CharacterBridge;
import org.rollinitiative.d20.entity.CharacterFactory;
import org.rollinitiative.d20.entity.InvalidEntityException;
import org.rollinitiative.d20.entity.NonPlayer;
import org.rollinitiative.d20.entity.NonPlayerBridge;
import org.rollinitiative.d20.entity.NonPlayerFactory;
import org.rollinitiative.d20.entity.Player;
import org.rollinitiative.d20.entity.classes.ClassContentBridge;
import org.rollinitiative.d20.entity.races.RaceContentBridge;
import org.rollinitiative.d20.entity.talents.TalentContentBridge;
import org.rollinitiative.d20.items.ItemContentBridge;
import org.rollinitiative.d20.persist.BridgeException;

public class Arena implements EncounterListener
{
   final static Log LOG = LogFactory.getLog(Arena.class);

   static XMLConfiguration config_;

   protected Dice dice = Dice.getInstance();

   protected Encounter encounter = null;
   
   protected EncounterAgent encounterAgent= null;

   protected ArrayList<Combatant> partyList = new ArrayList<Combatant>();

   protected ArrayList<Combatant> hostileList = new ArrayList<Combatant>();

   protected int partyHPs = 0;

   protected int hostileHPs = 0;

   boolean encounterComplete = false;

   protected HashMap<Combatant, String> actorStats = new HashMap<Combatant, String>();

   CharacterBridge characterBridge;

   CharacterFactory characterFactory;
   
   NonPlayerBridge npcBridge;

   NonPlayerFactory npcFactory;


   public Encounter getEncounter()
   {
      return encounter;
   }


   public ArrayList<Combatant> getPartyList()
   {
      return partyList;
   }


   public ArrayList<Combatant> getHostileList()
   {
      return hostileList;
   }


   public Combatant getPartyDefender()
   {
      int index = (int) (Math.random() * partyList.size());
      return partyList.get(index);
   }


   public Combatant getHostileDefender()
   {
      int index = (int) (Math.random() * hostileList.size());
      return hostileList.get(index);
   }


   public Combatant getStrongestHostileDefender()
   {
      int maxIndex = 0;

//      for (int i = 1; i < hostileList.size(); i++) {
//
//         if (hostileList.get(i).getActor().getHP() > hostileList.get(i - 1).getActor().getHP())
//            maxIndex = i;
//      }
      return hostileList.get(maxIndex);
   }


   public Combatant getWeakestHostileDefender()
   {
      int minIndex = 0;

//      for (int i = 1; i < hostileList.size(); i++) {
//         if (hostileList.get(i).getActor().getHP() < hostileList.get(i - 1).getActor().getHP())
//            minIndex = i;
//      }

      return hostileList.get(minIndex);
   }


   public void printHPs()
   {
      partyHPs = 0;
      hostileHPs = 0;

      System.out.println();
      Combatant[] combatants = encounter.getInitiativeOrder();
      for (int i = 0; i < combatants.length; i++) {
         Combatant combatant = combatants[i];
         String stats = actorStats.get(combatant);
         Actor actor = combatant.getActor();
         if (stats == null)
            stats = "";
//         if ((actor.getHP() >= 0) && (actor.getHP() < 10))
//            stats += " ";
//         stats += " " + actor.getHP();
//         actorStats.put(combatant, stats);
//         System.out.println(actor.getName() + "\t:" + stats + " /" + actor.getMaxHP());
//
//         if (actor.getHP() > 0) {
//            if (partyList.contains(combatant))
//               partyHPs += actor.getHP();
//            if (hostileList.contains(combatant))
//               hostileHPs += actor.getHP();
//         }
      }
      System.out.println();
   }


   public void execute()
   {
      encounterAgent.initialize();
      while (!encounterComplete) {
         // assign the action sets for all combatants (for now everyone attacks until dead)
         ActionSet actionSet = null;
         Combatant combatant = encounterAgent.advanceToNextCombatant();
         LOG.info("Next up is " + combatant.getActor().getName());
         actionSet = new ActionSet();
         actionSet.addAction(chooseAction(combatant));
         combatant.setActionSet(actionSet);
         encounterAgent.executeActionSet();
      }

   }


   /*
    * (non-Javadoc)
    * 
    * @see org.rollinitiative.d20.encounter.EncounterListener#endOfRound(int)
    */
   @Override
   public void endOfRound(int roundNum)
   {
      printHPs();
      LOG.info("Party/Hostile HP total: " + partyHPs + "/" + hostileHPs);
      if (partyHPs <= 0) {
         System.out.println("Party LOSES!");
         encounterComplete = true;
      }
      else if (hostileHPs <= 0) {
         System.out.println("Party WINS!");
         encounterComplete = true;
      }

      // if (roundNum > 1) {
      // encounterComplete = true;
      // }
   }


   public static void main(String[] args) throws Exception
   {
      DOMConfigurator.configure("log4j.xml");
      // Load the configuration file
      try {
         config_ = new XMLConfiguration("arena.config.xml");
      } catch (ConfigurationException configEx) {
         LOG.fatal("FAILED to load configuration file: config.xml");
      }

      int max = 1;
      int total = 0;

      for (int i = 0; i < max; i++) {
         Arena arena = new Arena();
         arena.init();
         arena.setupEncounter();
         arena.execute();
//         boolean partyWins = arena.execute();
//         if (partyWins)
//            total += 1;
      }
      LOG.info("Percent Party Wins = " + total + "/" + max);
   }


   void setupEncounter() throws BridgeException, InvalidEntityException
   {
      encounter = new Encounter("Arena");
      encounterAgent.setCurrentEncounter(encounter);
      encounterAgent.subscribe(this);

      // Assemble the party
      String partyMode = config_.getString("arena.party[@mode]");
      LOG.info("partyMode = " + partyMode);
      if (partyMode.equalsIgnoreCase("xmldb")) {
         loadParty(config_.getString("arena.party.collection"));
      }
      else {
         generateParty();
      }

      int hostileCount = 2;
      Combatant newCombatant = null;
      npcBridge.loadCollection("arena");
      for (int i = 0; i < hostileCount; i++) {
         try {
            NonPlayer rat = npcBridge.getNPCTemplateInstance(new QName("template.direAnimal.rat"));
            newCombatant = encounter.joinEncounter(rat, Faction.HOSTILE, true);
            hostileList.add(newCombatant);
            encounter.makeCombatantAware(rat.getActorID());
         } catch (Exception e) {
            LOG.error("Failed construction of hostile!", e);
         }

      }
   }


   void loadParty(String partyTag) throws BridgeException, InvalidEntityException
   {
      characterBridge.loadCollection("arena", partyTag);
      Player characters[] = characterBridge.getGroupPCs("party");
      LOG.debug("total number of characters in party = " + characters.length);
      Combatant newCombatant = null;
      for (Player player : characters) {
         newCombatant = encounter.joinEncounter(player, Faction.PARTY, true);
         partyList.add(newCombatant);
         encounter.makeCombatantAware(player.getActorID());
      }
   }


   void loadHostiles()
   {
      // TODO get this hostiles from a designated encounter
   }


   void generateParty()
   {
      Combatant newCombatant = null;

      Actor zephar = new Player("Player 1", "Zephar");
      zephar.setAbilityBaseValue(Ability.DEX, 14);
//      zephar.getHitPoints().advanceLevel(randomHP(3));
      newCombatant = encounter.joinEncounter(zephar, Faction.PARTY, true);
      partyList.add(newCombatant);
      encounter.makeCombatantAware(zephar.getActorID());

      Actor flint = new Player("Player 2", "Flint");
      flint.setAbilityBaseValue(Ability.STR, 18);
//      zephar.getHitPoints().advanceLevel(randomHP(5));
      newCombatant = encounter.joinEncounter(flint, Faction.PARTY, true);
      partyList.add(newCombatant);
      encounter.makeCombatantAware(flint.getActorID());
   }


   // temporary...
   int randomHP(int factor)
   {
      return 1 + (int) (10.0 * Math.random()) * factor;
   }


   Action chooseAction(Combatant combatant)
   {
      Attack action = new MeleeAttack();
      action.setAttacker(combatant);
      Faction faction = combatant.getAllegiance();
      if (faction == Faction.PARTY) {
         action.setDefender(getWeakestHostileDefender());
      }
      if (faction == Faction.HOSTILE) {
         action.setDefender(getPartyDefender());
      }
      return action;
   }


   void init()
   {
      // Configure bridges to content.
      RaceContentBridge raceBridge = new RaceContentBridge(config_);
      raceBridge.loadCollection("arena");

      ClassContentBridge classBridge = new ClassContentBridge(config_);
      classBridge.loadCollection("arena");

      TalentContentBridge skillBridge = new TalentContentBridge(config_);
      skillBridge.loadCollection("arena");

      ItemContentBridge itemBridge = new ItemContentBridge(config_);
      itemBridge.loadCollection("arena");

      // Configure CharacterFactory
      characterFactory = new CharacterFactory();
      characterFactory.setClassContentBridge(classBridge);
      characterFactory.setRaceContentBridge(raceBridge);
      characterFactory.setTalentBridge(skillBridge);
      characterFactory.setItemBridge(itemBridge);

      // Configure NonPlayerFactory
      npcFactory = new NonPlayerFactory();
      npcFactory.setClassContentBridge(classBridge);
      npcFactory.setRaceContentBridge(raceBridge);
      npcFactory.setTalentBridge(skillBridge);
      npcFactory.setItemBridge(itemBridge);

      characterBridge = new CharacterBridge(config_);
      characterBridge.setCharacterFactory(characterFactory);
      
      npcBridge = new NonPlayerBridge(config_);
      npcBridge.setNpcFactory(npcFactory);
      
      encounterAgent = new EncounterAgent();
   }
}
